package edu.yctc.delivery.controller.session;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Map;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.collect.Maps;

import edu.yctc.delivery.constant.ResultCode;
import edu.yctc.delivery.entity.ResultDO;
import edu.yctc.delivery.exception.DeliveryException;

/**
 * 
 * @className SessionContentHolder
 * @description sessonId对应业务id的托管
 * @author zyj
 * @date 2020年1月16日
 *
 */
public class SessionContentHolder {

    private final static Logger LOG =
        LoggerFactory.getLogger("serverLogger");
    /** 服务端最大session容量 */
    private static final int SESSION_MAX_QUANTITY = 10000;
    /** session失效判断时间周期为30分钟，单位为秒，此时间不能小于session失效时间 */
    private static final int SURVIVE_TIME = 30 * 60;

    /** sessionId对应登录用户userId的Map，sessionId -> userId */
    private static final Map<String, Long> SESSION_2_SIGN_IN_USER_ID_MAP = Maps.newHashMap();
    /** sessionId对应登录用户userLoginTime的Map，sessionId -> time */
    private static final Map<String, Long> SESSION_SIGN_IN_USER_TIME_MAP = Maps.newHashMap();
    /** sessionId对应找密改密的email的Map, sessionId -> email */
    private static final Map<String, String> SESSION_MAIL_MAP = Maps.newHashMap();
    /** sessionId对应找密改密的email的Map, sessionId -> phone */
    private static final Map<String, String> SESSION_PHONE_MAP = Maps.newHashMap();

    /** 失效session线程池 */
    private static final ScheduledExecutorService executorService = new ScheduledThreadPoolExecutor(1,
        new BasicThreadFactory.Builder().namingPattern("session-invalid-pool-%d").daemon(true).build());

    /**
     * SessionContentHolder类的构造方法
     */
    static {
        // 当内存中的session大于SESSION_MAX_QUANTITY，失效最老的session
        executorService.scheduleAtFixedRate(new Runnable() {

            @Override
            public void run() {
                ArrayList<Map.Entry<String, Long>> loginTimeList =
                    new ArrayList<>(SESSION_SIGN_IN_USER_TIME_MAP.entrySet());
                Collections.sort(loginTimeList, new Comparator<Map.Entry<String, Long>>() {

                    @Override
                    public int compare(Map.Entry<String, Long> o1, Map.Entry<String, Long> o2) // 升序
                    {
                        try {
                            if (o2.getValue() != null && o1.getValue() != null
                                && o2.getValue().compareTo(o1.getValue()) > 0) {
                                return 1;
                            } else {
                                return -1;
                            }
                        } catch (Exception e) {
                            LOG.error("login time sort error", e);
                        }
                        return 0;
                    }
                });

                int num = loginTimeList.size();
                // 判断是否大于最大容量并删除map中对应的session
                while (num > SESSION_MAX_QUANTITY) {
                    for (int i = 0; i <= num - SESSION_MAX_QUANTITY; i++) {
                        /** key是通过loginTimeList获得的对应要删除的sessionId */
                        String sessionId = loginTimeList.get(i).getKey();
                        SESSION_2_SIGN_IN_USER_ID_MAP.remove(sessionId);
                        SESSION_SIGN_IN_USER_TIME_MAP.remove(sessionId);
                        LOG.info("user session has been invalid, sessionId={}, last active time={}", sessionId,
                            loginTimeList.get(i).getValue());
                    }
                }

            }
        }, SURVIVE_TIME, SURVIVE_TIME, TimeUnit.SECONDS); // 延时30分钟后，按30分钟的周期执行任务
    }

    /**
     * 通过sessionId获取已经登录的userId
     *
     * @param sessionId
     * @return
     */
    public static long getSignInUserIdBySessionId(String sessionId) {
        if (StringUtils.isBlank(sessionId)) {
            throw new DeliveryException(ResultCode.PARAMETER_INVALID, ResultCode.MSG_PARAMETER_INVALID);
        }

        if (SESSION_2_SIGN_IN_USER_ID_MAP.containsKey(sessionId) == false) {
            throw new DeliveryException(ResultCode.USER_NOT_SIGN_IN, ResultCode.MSG_USER_NOT_SIGN_IN);
        }

        SESSION_SIGN_IN_USER_TIME_MAP.put(sessionId, System.currentTimeMillis());
        return SESSION_2_SIGN_IN_USER_ID_MAP.get(sessionId);
    }

    /**
     * 添加登录用户session
     *
     * @param sessionId
     * @param userId
     */
    public static void addSignInUserId(String sessionId, long userId) {
        if (StringUtils.isBlank(sessionId) || userId <= 0) {
            throw new DeliveryException(ResultCode.PARAMETER_INVALID, ResultCode.MSG_PARAMETER_INVALID);
        }

        // 如果有一致的sessionId，先删除老的（虽然几乎不可能）
        if (SESSION_2_SIGN_IN_USER_ID_MAP.containsKey(sessionId)) {
            SESSION_2_SIGN_IN_USER_ID_MAP.remove(sessionId);
        }

        SESSION_2_SIGN_IN_USER_ID_MAP.put(sessionId, userId);
        SESSION_SIGN_IN_USER_TIME_MAP.put(sessionId, System.currentTimeMillis());
    }

    /**
     * 用sessionId删除登录用户session
     *
     * @param sessionId
     */
    public static void deleteSignInBySessionId(String sessionId) {
        if (StringUtils.isBlank(sessionId)) {
            throw new DeliveryException(ResultCode.PARAMETER_INVALID, ResultCode.MSG_PARAMETER_INVALID);
        }

        if (!SESSION_2_SIGN_IN_USER_ID_MAP.containsKey(sessionId)) {
            return;
        }

        SESSION_2_SIGN_IN_USER_ID_MAP.remove(sessionId);
    }

    /**
     * 判断是否登陆,返回userDO
     *
     * @param sessionId
     * @return
     */
    public static ResultDO<Long> judgeSignin(String sessionId) {
        if (StringUtils.isBlank(sessionId)) {
            throw new DeliveryException(ResultCode.PARAMETER_INVALID, ResultCode.MSG_PARAMETER_INVALID);
        }
        if (SESSION_2_SIGN_IN_USER_ID_MAP.containsKey(sessionId)) {
            long id = SESSION_2_SIGN_IN_USER_ID_MAP.get(sessionId);
            if (id <= 0) {
                throw new DeliveryException(ResultCode.NO_SUCH_USER, ResultCode.MSG_NO_SUCH_USER);
            }
            return new ResultDO<Long>(true, ResultCode.SUCCESS, ResultCode.MSG_SUCCESS, id);
        }
        return new ResultDO<Long>(false, ResultCode.USER_NOT_SIGN_IN, ResultCode.MSG_USER_NOT_SIGN_IN, null);
    }

    /**
     * map中添加mail
     * 
     * @param sessionId
     * @param mail
     */
    public static void addMail(String sessionId, String mail) {
        if (StringUtils.isBlank(sessionId) || StringUtils.isBlank(mail)) {
            throw new DeliveryException(ResultCode.PARAMETER_INVALID, ResultCode.MSG_PARAMETER_INVALID);
        }
        if (SESSION_MAIL_MAP.containsKey(sessionId) || SESSION_MAIL_MAP.containsValue(mail)) {
            SESSION_MAIL_MAP.remove(sessionId);
        }
        SESSION_MAIL_MAP.put(sessionId, mail);

        executorService.schedule(new Runnable() {

            @Override
            public void run() {
                SessionContentHolder.deleteMailSessionBySessionId(sessionId);
                LOG.info("mail session has been deleted, sessionId={}", sessionId);
            }
        }, SURVIVE_TIME, TimeUnit.SECONDS);
    }

    /**
     * map中删除mail
     * 
     * @param sessionId
     */
    public static void deleteMailSessionBySessionId(String sessionId) {
        if (StringUtils.isEmpty(sessionId)) {
            throw new DeliveryException(ResultCode.PARAMETER_INVALID, ResultCode.MSG_PARAMETER_INVALID);
        }
        if (SESSION_MAIL_MAP.containsKey(sessionId) == false) {
            throw new DeliveryException(ResultCode.NO_SUCH_MAIL, ResultCode.MSG_NO_SUCH_MAIL);
        }
        SESSION_MAIL_MAP.remove(sessionId);
        executorService.schedule(new Runnable() {

            @Override
            public void run() {
                SessionContentHolder.deleteMailSessionBySessionId(sessionId);
                LOG.info("mail session has been deleted, sessionId={}", sessionId);
            }
        }, SURVIVE_TIME, TimeUnit.SECONDS);

    }

    public static String getMail(String sessionId) {
        if (StringUtils.isBlank(sessionId)) {
            throw new DeliveryException(ResultCode.PARAMETER_INVALID, ResultCode.MSG_PARAMETER_INVALID);
        }
        if (SESSION_MAIL_MAP.containsKey(sessionId) == false) {
            sessionId = null;
        }
        return SESSION_MAIL_MAP.get(sessionId);

    }

    /**
     * map中添加phone
     * 
     * @param sessionId
     * @param phone
     */
    public static void addPhone(String sessionId, String phone) {
        if (StringUtils.isBlank(sessionId) || StringUtils.isBlank(phone)) {
            throw new DeliveryException(ResultCode.PARAMETER_INVALID, ResultCode.MSG_PARAMETER_INVALID);
        }
        if (SESSION_PHONE_MAP.containsKey(sessionId) || SESSION_PHONE_MAP.containsValue(phone)) {
            SESSION_PHONE_MAP.remove(sessionId);
        }
        SESSION_PHONE_MAP.put(sessionId, phone);

        executorService.schedule(new Runnable() {

            @Override
            public void run() {
                SessionContentHolder.deleteMailSessionBySessionId(sessionId);
                LOG.info("phone session has been deleted, sessionId={}", sessionId);
            }
        }, SURVIVE_TIME, TimeUnit.SECONDS);
    }

    /**
     * map中删除phone
     * 
     * @param sessionId
     */
    public static void deletePhoneSessionBySessionId(String sessionId) {
        if (StringUtils.isEmpty(sessionId)) {
            throw new DeliveryException(ResultCode.PARAMETER_INVALID, ResultCode.MSG_PARAMETER_INVALID);
        }
        if (SESSION_PHONE_MAP.containsKey(sessionId) == false) {
            throw new DeliveryException(ResultCode.NO_SUCH_PHONE, ResultCode.MSG_NO_SUCH_PHONE);
        }
        SESSION_PHONE_MAP.remove(sessionId);
        executorService.schedule(new Runnable() {

            @Override
            public void run() {
                SessionContentHolder.deleteMailSessionBySessionId(sessionId);
                LOG.info("phone session has been deleted, sessionId={}", sessionId);
            }
        }, SURVIVE_TIME, TimeUnit.SECONDS);

    }

    public static String getPhone(String sessionId) {
        if (StringUtils.isBlank(sessionId)) {
            throw new DeliveryException(ResultCode.PARAMETER_INVALID, ResultCode.MSG_PARAMETER_INVALID);
        }
        if (SESSION_PHONE_MAP.containsKey(sessionId) == false) {
            sessionId = null;
        }
        return SESSION_PHONE_MAP.get(sessionId);

    }
}
